package views.folder
{
	import flash.display.DisplayObject;
	import flash.display.Shape;
	import flash.display.Sprite;
	import flash.events.Event;
	import flash.geom.Matrix;
	import flash.geom.Point;
	import flash.geom.Rectangle;
	
	import gs.TweenLite;
	import gs.easing.Back;
	
	[Event(name="complete",type="flash.events.Event")]
	
	public class AutoFoldView extends Sprite implements I_FoldableView
	{
		public const unfoldSkew:Number = 0;
		public const unfoldWidthRate:Number = 1;
		public const foldSkew:Number = 0.1;
		
		private const FOLD_DURATION:Number = 1;
		
		private var evenFoldMatrix:Matrix;
		private var evenUnfoldMatrix:Matrix
		private var oddFoldMatrix:Matrix;
		private var oddUnfoldMatrix:Matrix;
		private var currentEvenMatrix:Matrix;
		private var currentOddMatrix:Matrix;
		
		private var contentRect:Rectangle;
		private var paging:Boolean;
		private var colWidth:Number;
		private var rowHeight:Number;
		
		private var itemMask:Shape;
		
		private var items:Vector.<Vector.<DisplayObject>>;
		private var itemContainer:Sprite;
		
		private var isMoving:Boolean;
		
		private var evenItemComplete:Boolean;
		private var oddItemComplete:Boolean;
		
		private var foldCallback:Function;
		private var unfoldCallback:Function;
		
		private var isFold:Boolean;
		
		
		public function AutoFoldView()
		{
		}
		
		private function getMatrix(points:Array,oriPoints:Array):Matrix
		{
			var a:Number = ( (points[0].x-points[1].x)*(oriPoints[1].y-oriPoints[2].y) - (points[1].x-points[2].x)*(oriPoints[0].y-oriPoints[1].y) ) /
				( (oriPoints[0].x-oriPoints[1].x)*(oriPoints[1].y-oriPoints[2].y) - (oriPoints[1].x-oriPoints[2].x)*(oriPoints[0].y-oriPoints[1].y) );
			var c:Number = ( (points[0].x - points[1].x) - (oriPoints[0].x - oriPoints[1].x) * a) / (oriPoints[0].y - oriPoints[1].y);
			var tx:Number = points[0].x - c * oriPoints[0].y - a * oriPoints[0].x;
			
			var b:Number = ( (points[0].y-points[1].y)*(oriPoints[1].y-oriPoints[2].y) - (points[1].y-points[2].y)*(oriPoints[0].y-oriPoints[1].y) ) /
				( (oriPoints[0].x-oriPoints[1].x)*(oriPoints[1].y-oriPoints[2].y) - (oriPoints[1].x-oriPoints[2].x)*(oriPoints[0].y-oriPoints[1].y) );
			var d:Number = ( (points[0].y - points[1].y) - (oriPoints[0].x - oriPoints[1].x) * b) / (oriPoints[0].y - oriPoints[1].y);
			var ty:Number = points[0].y - d * oriPoints[0].y - b * oriPoints[0].x;
			
			
			return new Matrix(a,b,c,d,tx,ty);
		} 
		
		// items order by column.
		public function initialize(items:Vector.<Vector.<DisplayObject>>,contentRect:Rectangle,colWidth:Number,rowHeight:Number,paging:Boolean = true):void
		{
			this.contentRect = contentRect;
			this.items = items;
			this.paging = paging;
			this.colWidth = colWidth;
			this.rowHeight = rowHeight;
			
			if(itemContainer != null)
				removeChild(itemContainer);
			
			itemContainer = new Sprite;
			itemContainer.visible = false;
			addChild(itemContainer);
			
			if(itemMask != null)
				removeChild(itemMask);
				
			itemMask = new Shape;
			itemMask.graphics.beginFill(0xFF9900,.3);
			itemMask.graphics.drawRect(contentRect.x,contentRect.y,contentRect.width + colWidth,contentRect.height);
			itemMask.graphics.endFill();
			addChild(itemMask);
			
			itemContainer.mask = itemMask;
			
			//calculate matrix.
			var sampPoints:Array = [new Point(0,0),new Point(0,rowHeight),new Point(colWidth,rowHeight)];
			oddFoldMatrix = getMatrix([new Point(0,rowHeight * foldSkew),
				new Point(0,rowHeight * (1 + foldSkew)),
				new Point(0,rowHeight)],
				sampPoints);
			oddUnfoldMatrix = getMatrix([new Point(0,rowHeight * unfoldSkew),
				new Point(0,rowHeight * (1 + unfoldSkew)),
				new Point(colWidth * unfoldWidthRate,rowHeight)],
				sampPoints);
			evenFoldMatrix = getMatrix([new Point(0,0),
				new Point(0,rowHeight),
				new Point(0,rowHeight * (1 + foldSkew))],
				sampPoints);
			evenUnfoldMatrix = getMatrix([new Point(0,0),
				new Point(0,rowHeight),
				new Point(colWidth * unfoldWidthRate,rowHeight * (1 + unfoldSkew))],
				sampPoints);
			
			currentEvenMatrix = evenFoldMatrix.clone();
			currentOddMatrix = oddFoldMatrix.clone();
			isFold = true;
			isMoving = false;
			
			this.present();
		}
		
		private function present():void
		{
			var colIndex:int = 0;
			var startX:Number = 0;
			for each(var itemRow:Vector.<DisplayObject> in items)
			{
				var rowIndex:int = 0;
				var startY:Number = 0;
				for each(var itemView:DisplayObject in itemRow)
				{
					var matrix:Matrix = colIndex % 2 == 0 ? currentEvenMatrix.clone() : currentOddMatrix.clone();
					matrix.concat(new Matrix(1,0,0,1,startX,startY));
					
					itemView.transform.matrix = matrix;
					itemView.alpha = 1 * (itemView.width / (colWidth * unfoldWidthRate));
					itemContainer.addChild(itemView );
					
					startY = matrix.transformPoint(new Point(colIndex % 2 == 0 ? 0 : colWidth,rowHeight)).y;
					rowIndex++;
					
					if(rowIndex >= itemRow.length)
						startX = matrix.transformPoint(new Point(colWidth,0)).x;
				}
				colIndex ++;
			}
		}
		
		private function checkComplete(categroy:String = "even"):void
		{
			if(categroy == "even")
				evenItemComplete = true;
			if(categroy == "odd")
				oddItemComplete = true;
			
			if(evenItemComplete && oddItemComplete)
			{
				isMoving = false;
				dispatchEvent(new Event(Event.COMPLETE));
			}
			
			itemContainer.visible = !isFold;
			
			if(isFold && foldCallback is Function)
			{
				foldCallback.call(null,this);
				foldCallback = null;
			}
				
			if(!isFold && unfoldCallback is Function)
			{
				unfoldCallback.call(null,this);
				unfoldCallback = null;
			}
		}
		
		public function fold(animated:Boolean = true,callback:Function = null):void
		{
			
			if(isFold)
				return;
			isFold = true;
			
			foldCallback = callback;
			
			TweenLite.killTweensOf(currentEvenMatrix);
			TweenLite.killTweensOf(currentOddMatrix);
			
			if(animated)
			{
				isMoving = true;
				itemContainer.visible = true;
				
				evenItemComplete = false;
				oddItemComplete = false;
				
				TweenLite.to(currentEvenMatrix,FOLD_DURATION,
					{ease:Back.easeInOut,a:evenFoldMatrix.a,b:evenFoldMatrix.b,
						c:evenFoldMatrix.c,d:evenFoldMatrix.d,tx:evenFoldMatrix.tx,
						ty:evenFoldMatrix.ty,onUpdate:present,onComplete:checkComplete,onCompleteParams:["even"]});
				
				TweenLite.to(currentOddMatrix,FOLD_DURATION,
					{ease:Back.easeInOut,a:oddFoldMatrix.a,b:oddFoldMatrix.b,
						c:oddFoldMatrix.c,d:oddFoldMatrix.d,tx:oddFoldMatrix.tx,
						ty:oddFoldMatrix.ty,onUpdate:present,onComplete:checkComplete,onCompleteParams:["odd"]});				
			}else{
				currentEvenMatrix = evenFoldMatrix.clone();
				currentOddMatrix = oddFoldMatrix.clone();
				present();
				checkComplete("odd");
				checkComplete("even");
			}
		}
		
		public function unfold(animated:Boolean = true,callback:Function = null):void
		{
			if(!isFold)
				return;
			isFold = false;
			
			unfoldCallback = callback;
			
			TweenLite.killTweensOf(currentEvenMatrix);
			TweenLite.killTweensOf(currentOddMatrix);
			
			if(animated)
			{
				isMoving = true;
				itemContainer.visible = true;
				
				evenItemComplete = false;
				oddItemComplete = false;
				
				TweenLite.to(currentEvenMatrix,FOLD_DURATION,
					{ease:Back.easeInOut,a:evenUnfoldMatrix.a,b:evenUnfoldMatrix.b,
						c:evenUnfoldMatrix.c,d:evenUnfoldMatrix.d,tx:evenUnfoldMatrix.tx,
						ty:evenUnfoldMatrix.ty,onUpdate:present,onComplete:checkComplete,onCompleteParams:["even"]});
				
				TweenLite.to(currentOddMatrix,FOLD_DURATION,
					{ease:Back.easeInOut,a:oddUnfoldMatrix.a,b:oddUnfoldMatrix.b,
						c:oddUnfoldMatrix.c,d:oddUnfoldMatrix.d,tx:oddUnfoldMatrix.tx,
						ty:oddUnfoldMatrix.ty,onUpdate:present,onComplete:checkComplete,onCompleteParams:["odd"]});
			}else{
				currentEvenMatrix = evenUnfoldMatrix.clone();
				currentOddMatrix = oddUnfoldMatrix.clone();
				present();
				checkComplete("odd");
				checkComplete("even");
			}
			
		}
		
		public function get isFolded():Boolean
		{
			return isFold;
		}
		
	}
}